#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <windows.h>
#include <time.h>
#include <stdbool.h>

// 定義遊戲畫面大小
#define WIDTH 60
#define HEIGHT 20

// 方向控制
#define UP 1
#define DOWN 2
#define LEFT 3
#define RIGHT 4

// 蛇和食物符號
#define SNAKE_HEAD 'O'    // 蛇頭
#define SNAKE_BODY 'o'    // 蛇身
#define FOOD '@'          // 食物
#define WALL '#'          // 牆壁

// 遊戲狀態變量
int score = 0;           // 當前分數
int highscore = 0;       // 最高分數
int direction = RIGHT;   // 當前移動方向
int sleeptime = 150;     // 控制蛇移動速度(毫秒)
bool gameover = false;   // 遊戲結束標誌

// 蛇結構體
struct Snake {
    int x;              // x坐標
    int y;              // y坐標
    struct Snake* next; // 指向下一節點的指針
};

// 全局蛇指針
struct Snake* head;     // 蛇頭指針
struct Snake* tail;     // 蛇尾指針
int snakeLength = 1;    // 蛇長度

// 食物位置
int foodX, foodY;

// 函數聲明
void printsnake();          // 打印蛇的ASCII藝術
void welcometogame();       // 歡迎界面
void createMap();           // 創建遊戲地圖
void scoreandtips();        // 顯示分數和提示
void File_out();            // 從文件讀取最高分
void initsnake();           // 初始化蛇
void createfood();          // 創建食物
int biteself();             // 檢查是否咬到自己
void cantcrosswall();       // 檢查是否撞牆
void speedup();             // 加速
void speeddown();           // 減速
void snakemove();           // 蛇移動邏輯
void keyboardControl();     // 鍵盤控制
void gameoverInterface();   // 遊戲結束界面
void explation();           // 遊戲說明

// 設置光標位置
void gotoxy(int x, int y) {
    COORD coord;
    coord.X = x;
    coord.Y = y;
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
}

// 設置文字顏色
void setColor(int color) {
    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), color);
}

// 隱藏光標
void hideCursor() {
    CONSOLE_CURSOR_INFO cursor_info = {1, 0};
    SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);
}

// 打印蛇的ASCII藝術(歡迎界面用)
void printsnake() {
    setColor(10); // 綠色
    gotoxy(25, 5);
    printf("  _________         _________");
    gotoxy(25, 6);
    printf(" /         \\       /         \\");
    gotoxy(25, 7);
    printf("/  /~~~~~\\  \\     /  /~~~~~\\  \\");
    gotoxy(25, 8);
    printf("|  |     |  |     |  |     |  |");
    gotoxy(25, 9);
    printf("|  |     |  |     |  |     |  |");
    gotoxy(25, 10);
    printf("|  |     |  |     |  |     |  |");
    gotoxy(25, 11);
    printf("|  |     |  |     |  |     |  |");
    gotoxy(25, 12);
    printf("|  \\_____/  \\_____/  \\_____/  |");
    gotoxy(25, 13);
    printf(" \\___________________________/");
    setColor(7); // 恢復默認顏色
}

// 歡迎界面
void welcometogame() {
    system("cls");  // 清屏
    printsnake();   // 打印蛇圖案
    
    setColor(12); // 紅色
    gotoxy(30, 15);
    printf("貪吃蛇遊戲");
    
    setColor(14); // 黃色
    gotoxy(25, 17);
    printf("1. 開始遊戲");
    gotoxy(25, 18);
    printf("2. 遊戲說明");
    gotoxy(25, 19);
    printf("3. 最高分");
    gotoxy(25, 20);
    printf("4. 退出遊戲");
    
    setColor(11); // 淺藍色
    gotoxy(25, 22);
    printf("請選擇 (1-4): ");
    
    char choice = getch();  // 獲取用戶輸入
    switch(choice) {
        case '1':
            // 重置遊戲狀態
            gameover = false;
            score = 0;
            direction = RIGHT;
            sleeptime = 150;
            snakeLength = 1;
            
            createMap();    // 創建地圖
            initsnake();    // 初始化蛇
            createfood();  // 創建食物
            snakemove();    // 開始遊戲循環
            break;
        case '2':
            explation();    // 顯示遊戲說明
            break;
        case '3':
            File_out();     // 讀取最高分
            gotoxy(25, 24);
            printf("最高分: %d", highscore);
            gotoxy(25, 25);
            printf("按任意鍵返回...");
            getch();
            welcometogame();  // 返回歡迎界面
            break;
        case '4':
            exit(0);        // 退出遊戲
        default:
            welcometogame();  // 無效輸入重新顯示
    }
}

// 創建遊戲地圖(繪製邊界)
void createMap() {
    system("cls");  // 清屏
    hideCursor();   // 隱藏光標
    
    // 繪製上下邊界
    for (int i = 0; i < WIDTH; i++) {
        gotoxy(i, 0);
        printf("%c", WALL);
        gotoxy(i, HEIGHT - 1);
        printf("%c", WALL);
    }
    
    // 繪製左右邊界
    for (int i = 0; i < HEIGHT; i++) {
        gotoxy(0, i);
        printf("%c", WALL);
        gotoxy(WIDTH - 1, i);
        printf("%c", WALL);
    }
}

// 顯示分數和操作提示
void scoreandtips() {
    gotoxy(WIDTH + 5, 5);
    setColor(14);
    printf("當前得分: %d   ", score);  // 添加空格清除舊內容
    
    gotoxy(WIDTH + 5, 7);
    printf("最高得分: %d   ", highscore);
    
    gotoxy(WIDTH + 5, 10);
    setColor(11);
    printf("控制方向:");
    gotoxy(WIDTH + 5, 11);
    printf("W/↑ - 上");
    gotoxy(WIDTH + 5, 12);
    printf("S/↓ - 下");
    gotoxy(WIDTH + 5, 13);
    printf("A/← - 左");
    gotoxy(WIDTH + 5, 14);
    printf("D/→ - 右");
    
    gotoxy(WIDTH + 5, 16);
    setColor(13);
    printf("加速: F1");
    gotoxy(WIDTH + 5, 17);
    printf("減速: F2");
    gotoxy(WIDTH + 5, 18);
    printf("退出: ESC");
}

// 從文件讀取最高分
void File_out() {
    FILE *fp = fopen("save.txt", "r");
    if (fp != NULL) {
        fscanf(fp, "%d", &highscore);
        fclose(fp);
    }
}

// 初始化蛇
void initsnake() {
    // 釋放舊蛇(如果存在)
    if (head != NULL) {
        struct Snake* current = head;
        while (current != NULL) {
            struct Snake* temp = current;
            current = current->next;
            free(temp);
        }
        head = NULL;
        tail = NULL;
    }
    
    // 創建蛇頭
    head = (struct Snake*)malloc(sizeof(struct Snake));
    head->x = 24;
    head->y = 5;
    head->next = NULL;
    tail = head;
    
    // 初始蛇身(3節)
    for (int i = 1; i <= 3; i++) {
        struct Snake* newBody = (struct Snake*)malloc(sizeof(struct Snake));
        newBody->x = head->x - i * 2;  // 每節間隔2個字符
        newBody->y = head->y;
        newBody->next = head;
        head = newBody;
        snakeLength++;
    }
    
    // 繪製蛇
    struct Snake* current = head;
    setColor(14); // 黃色
    while (current != NULL) {
        gotoxy(current->x, current->y);
        if (current == head) {
            printf("%c", SNAKE_HEAD);  // 蛇頭
        } else {
            printf("%c", SNAKE_BODY);  // 蛇身
        }
        current = current->next;
    }
}

// 創建食物
void createfood() {
    srand(time(NULL));  // 初始化隨機種子
    
    bool validPosition = false;
    while (!validPosition) {
        // 確保食物x坐標為偶數(與蛇對齊)
        foodX = 2 + 2 * (rand() % ((WIDTH - 4) / 2));
        foodY = 2 + rand() % (HEIGHT - 4);
        
        // 確保食物不會出現在蛇身上
        struct Snake* current = head;
        validPosition = true;
        while (current != NULL) {
            if (current->x == foodX && current->y == foodY) {
                validPosition = false;
                break;
            }
            current = current->next;
        }
    }
    
    setColor(12); // 紅色
    gotoxy(foodX, foodY);
    printf("%c", FOOD);
}

// 檢查是否咬到自己
int biteself() {
    struct Snake* current = head->next; // 從蛇身開始檢查
    while (current != NULL) {
        if (head->x == current->x && head->y == current->y) {
            return 1;  // 咬到自己
        }
        current = current->next;
    }
    return 0;  // 沒有咬到自己
}

// 檢查是否撞牆
void cantcrosswall() {
    if (head->x <= 0 || head->x >= WIDTH - 1 || 
        head->y <= 0 || head->y >= HEIGHT - 1) {
        gameover = true;  // 撞牆遊戲結束
    }
}

// 加速
void speedup() {
    if (sleeptime > 50) {  // 最小速度限制
        sleeptime -= 20;   // 減少延遲時間
        score += 2;        // 加速時額外加分
    }
}

// 減速
void speeddown() {
    if (sleeptime < 300) {  // 最大速度限制
        sleeptime += 20;    // 增加延遲時間
        if (score > 0) score -= 1; // 減速時減分
    }
}

// 蛇移動
void snakemove() {
    while (!gameover) {
        keyboardControl();  // 處理鍵盤輸入
        scoreandtips();     // 更新分數顯示
        
        // 計算新頭部位置
        int newX = head->x;
        int newY = head->y;
        
        switch (direction) {
            case UP:    newY--; break;
            case DOWN:  newY++; break;
            case LEFT:  newX -= 2; break; // 橫向移動2格保持比例
            case RIGHT: newX += 2; break;
        }
        
        // 創建新頭部
        struct Snake* newHead = (struct Snake*)malloc(sizeof(struct Snake));
        newHead->x = newX;
        newHead->y = newY;
        newHead->next = head;
        head = newHead;
        
        // 檢查是否吃到食物
        if (head->x == foodX && head->y == foodY) {
            score += 5;
            createfood();      // 創建新食物
            snakeLength++;     // 增加蛇長度
        } else {
            // 沒吃到食物，移除尾部
            struct Snake* current = head;
            while (current->next != tail) {
                current = current->next;
            }
            
            // 清除尾部
            gotoxy(tail->x, tail->y);
            printf(" ");
            
            free(tail);       // 釋放尾部內存
            current->next = NULL;
            tail = current;
        }
        
        // 繪製新頭部
        setColor(14);
        gotoxy(head->x, head->y);
        printf("%c", SNAKE_HEAD);
        
        // 更新原頭部為身體
        gotoxy(head->next->x, head->next->y);
        printf("%c", SNAKE_BODY);
        
        // 檢查碰撞
        cantcrosswall();  // 檢查撞牆
        if (biteself()) { // 檢查咬到自己
            gameover = true;
        }
        
        // 延遲
        Sleep(sleeptime);
        
        if (gameover) {
            gameoverInterface();  // 顯示遊戲結束界面
        }
    }
}

// 鍵盤控制
void keyboardControl() {
    // 上
    if ((GetAsyncKeyState('W') & 0x8000) || (GetAsyncKeyState(VK_UP) & 0x8000)) {
        if (direction != DOWN) direction = UP;  // 不能直接反向移動
    }
    // 下
    else if ((GetAsyncKeyState('S') & 0x8000) || (GetAsyncKeyState(VK_DOWN) & 0x8000)) {
        if (direction != UP) direction = DOWN;
    }
    // 左
    else if ((GetAsyncKeyState('A') & 0x8000) || (GetAsyncKeyState(VK_LEFT) & 0x8000)) {
        if (direction != RIGHT) direction = LEFT;
    }
    // 右
    else if ((GetAsyncKeyState('D') & 0x8000) || (GetAsyncKeyState(VK_RIGHT) & 0x8000)) {
        if (direction != LEFT) direction = RIGHT;
    }
    // F1加速
    if (GetAsyncKeyState(VK_F1) & 0x8000) {
        speedup();
    }
    // F2減速
    if (GetAsyncKeyState(VK_F2) & 0x8000) {
        speeddown();
    }
    // ESC退出
    if (GetAsyncKeyState(VK_ESCAPE) & 0x8000) {
        gameover = true;
    }
}

// 遊戲結束界面
void gameoverInterface() {
    system("cls");
    
    setColor(12);
    gotoxy(25, 5);
    printf("遊戲結束!");
    
    gotoxy(25, 7);
    printf("你的得分: %d", score);
    
    // 更新最高分
    if (score > highscore) {
        highscore = score;
        FILE *fp = fopen("save.txt", "w");
        if (fp != NULL) {
            fprintf(fp, "%d", highscore);
            fclose(fp);
        }
        
        setColor(10);
        gotoxy(25, 9);
        printf("創紀錄啦！最高分被你刷新啦，真棒！！！");
    } else {
        setColor(11);
        gotoxy(25, 9);
        printf("繼續努力哦~你離最高分還差: %d分", highscore - score);
    }
    
    setColor(14);
    gotoxy(25, 12);
    printf("1. 重新開始");
    gotoxy(25, 13);
    printf("2. 退出遊戲");
    
    gotoxy(25, 15);
    printf("請選擇 (1-2): ");
    
    char choice;
    do {
        choice = getch();
    } while (choice != '1' && choice != '2');
    
    if (choice == '1') {
        // 重置遊戲狀態
        gameover = false;
        score = 0;
        direction = RIGHT;
        sleeptime = 150;
        snakeLength = 1;
        
        // 釋放蛇內存
        if (head != NULL) {
            struct Snake* current = head;
            while (current != NULL) {
                struct Snake* temp = current;
                current = current->next;
                free(temp);
            }
            head = NULL;
            tail = NULL;
        }
        
        welcometogame();  // 返回歡迎界面
    } else {
        exit(0);  // 退出遊戲
    }
}

// 遊戲說明界面
void explation() {
    system("cls");
    
    setColor(11);
    gotoxy(20, 2);
    printf("遊戲說明");
    
    setColor(7);
    gotoxy(5, 4);
    printf("1. 使用 W/A/S/D 或方向鍵控制蛇的移動方向");
    gotoxy(5, 6);
    printf("2. 吃到食物(@)可以增加分數和蛇的長度");
    gotoxy(5, 8);
    printf("3. 撞到牆壁或自己的身體遊戲結束");
    gotoxy(5, 10);
    printf("4. 按F1加速，按F2減速");
    gotoxy(5, 12);
    printf("5. 按ESC鍵可以隨時退出遊戲");
    
    setColor(14);
    gotoxy(5, 16);
    printf("按任意鍵返回主菜單...");
    getch();
    welcometogame();
}

int main() {
    // 設置控制台窗口大小
    system("mode con cols=90 lines=30");
    
    // 設置窗口標題
    SetConsoleTitle("貪吃蛇遊戲");
    
    // 初始化隨機種子
    srand(time(NULL));
    
    // 讀取最高分
    File_out();
    
    // 進入歡迎界面
    welcometogame();
    
    return 0;
}